#include "rtsp.h"
#include "rtsp_client.h"
#include "util.h"
#include "uv.h"
#include <list>

uv_loop_t* _uv_loop_;
bool _inner_uv_;

enum event_type_t {
    event_play = 0,
    event_stop
};

typedef struct async_event_t{
    event_type_t   type;
    async_event_t *next;
    RTSP_REQUEST  *req;
    async_event_t()
        : type(event_play)
        , next(NULL)
        , req(NULL)
    {}
};

uv_async_t    _uv_async_;
//async_event_t *_event_list_ = NULL;
//async_event_t *_event_end_ = NULL;
list<async_event_t*> _event_list;
uv_mutex_t    _event_mutex_;

static void run_loop_thread(void* arg)
{
    while (_inner_uv_) {
        uv_run(_uv_loop_, UV_RUN_DEFAULT);
        Sleep(200);
    }
    uv_loop_close(_uv_loop_);
}

static void on_uv_async(uv_async_t* handle){
    uv_mutex_lock(&_event_mutex_);
    //for (async_event_t *it = _event_list_; it != NULL; it = it->next){
    //    if(it->type == event_play) {
    //        RTSP_REQUEST *option = (RTSP_REQUEST*)it->req;
    //        CRtspClient* c = new CRtspClient(option);
    //        c->play();
    //    } else if(it->type == event_stop){
    //        RTSP_REQUEST *option = (RTSP_REQUEST*)it->req;
    //         CRtspClient* c = (CRtspClient*)option->client;
    //         c->stop();
    //    }
    //}
    //for (async_event_t *it = _event_list_; it != NULL; ){
    //    async_event_t *tmp = it;
    //    it = it->next;
    //    delete tmp;
    //}
    //_event_list_ = NULL;
    //_event_end_  = NULL;
    //Log::debug("async events:%d", _event_list.size());
    for(auto e:_event_list) {
        //Log::debug("a event");
        if(e->type == event_play) {
            RTSP_REQUEST *option = (RTSP_REQUEST*)e->req;
            CRtspClient* c = new CRtspClient(option);
            c->play();
        } else if(e->type == event_stop){
            RTSP_REQUEST *option = (RTSP_REQUEST*)e->req;
            CRtspClient* c = (CRtspClient*)option->client;
            c->stop();
        }
        delete e;
    }
    _event_list.clear();
    uv_mutex_unlock(&_event_mutex_);
}

int set_uv(void* uv) {
    if(uv){
        _uv_loop_ = (uv_loop_t*)uv;
    } else {
        uv_thread_t tid;
        _uv_loop_ = uv_loop_new();
        _inner_uv_ = true;
        int ret = uv_thread_create(&tid, run_loop_thread, 0);
        if(ret < 0) {
            printf("uv thread creat failed: %s\n", uv_strerror(ret));
            uv_loop_close(_uv_loop_);
            free(_uv_loop_);
            return ret;
        }
    }
    uv_async_init(_uv_loop_, &_uv_async_, on_uv_async);
    uv_mutex_init(&_event_mutex_);
    return 0;
}

void stop_uv() {
    _inner_uv_ = false;
    uv_stop(_uv_loop_);
}

int rtsp_play(RTSP_REQUEST *option) {
    uv_mutex_lock(&_event_mutex_);
    async_event_t *e = new async_event_t;
    e->type = event_play;
    e->req = option;
    //if(_event_list_ == NULL) {
    //    _event_list_ = e;
    //    _event_end_ = e;
    //} else {
    //    _event_end_->next = e;
    //    _event_end_ = e;
    //}
    _event_list.push_back(e);
    uv_mutex_unlock(&_event_mutex_);
    uv_async_send(&_uv_async_);
    return 0;
}

int rtsp_stop(RTSP_REQUEST *option) {
    uv_mutex_lock(&_event_mutex_);
    async_event_t *e = new async_event_t;
    e->type = event_stop;
    e->req = option;
    //if(_event_list_ == NULL) {
    //    _event_list_ = e;
    //    _event_end_ = e;
    //} else {
    //    _event_end_->next = e;
    //    _event_end_ = e;
    //}
    _event_list.push_back(e);
    uv_mutex_unlock(&_event_mutex_);
    uv_async_send(&_uv_async_);
    return 0;
}

char* rtsp_errors[] = {
    "connect to server failed",
    "tcp receiving failed",
    "make ip4 address failed",
    "tcp sending failed",
    "rtsp option method failed",
    "rtsp describe method failed",
    "rtsp describe method failed Authenticate",
    "rtsp setup method failed",
    "rtsp play method failed",
    "rtsp teardown method failed",
};

char* rtsp_strerr(int status) {
    if(status == 0) {
        return "success";
    }
    if(status >= RTSP_ERR_MAX) {
        return "unknown error";
    }
    return rtsp_errors[status + 1000];
}

char* rtsp_step[] = {
    "invalid step",
    "option",
    "describe",
    "setup",
    "play",
    "teardown"
};

char* rtsp_strstep(int step) {
    if(step > RTSP_STEP_TEARDOWN)
        step = 0;

    return rtsp_step[step];
}